﻿#include "CSharpCodeGenerator.h"
#include "XyNetCompiler/ServiceParser.h"

namespace XyNet
{

	void CSharpCodeGenerator::generator(ServiceParser* parser)
	{
		auto serviceFilePath = parser->getServiceFilePath();
		auto basePath = serviceFilePath.parent_path() / "csharp";
		filesystem::create_directories(basePath);
		basePath /= serviceFilePath.stem();
		filesystem::path csPath = basePath.string() + ".service.cs";

		filesystem::ofstream csStream(csPath);

		// 写入必须的引用
		csStream << "using System.Diagnostics;" "\n\n";

		// 写入package
		auto package = parser->getPackage();
		if (!package.empty())
		{
			csStream << boost::format("namespace %s" "\n") % package << "{" "\n\n";
		}

		// 写入服务
		auto services = parser->getServices();
		for (const auto& service : *services)
		{
			csStream << boost::format(
				"public abstract class %s : XyNet.RpcService" "\n") % service._name << 
				"{" "\n\n";

			// 写入方法
			for (const auto& method : service._methods)
			{
				csStream << boost::format(
					"\t" "public class %sMethod : XyNet.RpcMethod" "\n") % method._name <<
					"\t" "{" "\n";

				// 请求应答
				csStream <<
					boost::format("\t\t" "using Request = %sMessage.%sRequest;" "\n") %
					service._name % method._name <<
					boost::format("\t\t" "using Response = %sMessage.%sResponse;" "\n\n") %
					service._name % method._name;

				// 控制器
				if (method._errors.empty())
					csStream << "\t\t" "using Controller = XyNet.RpcController;" "\n\n";
				else
				{
					csStream <<
						"\t\t" "class Controller : public XyNet.RpcController" "\n"
						"\t\t" "{" "\n"
						"\t\t\t" "public Controller(XyNet.RpcSocketChannel socketChannel);" "\n\n"
						"\t\t\t" "public virtual XyNet.RpcError createError(int code) const override;" "\n\n";
					for (const auto& error : method._errors)
					{
						csStream << boost::format(
							"\t\t\t" "public static const int %s = 0x%s;" "\n") %
							error._name % error._codeString;
					}
					csStream << "\t\t" "};" "\n\n";
				}

				// 参数
				csStream <<
					"\t\t" "public class Argument : XyNet.RpcArgument" "\n"
					"\t\t" "{" "\n"
					"\t\t\t" "public Argument() : base(new Request(), new Response()) {}""\n\n"
					"\t\t\t" "public new Controller Controller { get { return _controller as Controller; } }" "\n"
					"\t\t\t" "public new Request Request { get { return _request as Request; } }" "\n"
					"\t\t\t" "public new Response Response { get { return _response as Response; } }" "\n\n"
					"\t\t\t" "public MessageParser<Request> RequestParser { get { return Request.Parser; } }" "\n"
					"\t\t\t" "public MessageParser<Response> ResponseParser { get { return Response.Parser; } }" "\n\n"
					"\t\t\t" "private override XyNet.RpcArgument clone();" "\n"
					"\t\t\t" "private override void initializeController(XyNet.RpcSocketChannel socketChannel);" "\n"
					"\t\t" "};" "\n\n";

				// 回调
				csStream <<
					"\t\t" "public class Callback : XyNet.RpcCallback" "\n"
					"\t\t" "{" "\n"
					"\t\t\t" "public delegate void ResponseDelegate(Argument argument);" "\n"
					"\t\t\t" "public delegate void ExceptionDelegate(Argument argument);" "\n\n"
					"\t\t\t" "public Callback(XyNet.RpcArgument argument, ResponseDelegate responseHandler, ExceptionDelegate exceptionHandler)" "\n"
					"\t\t\t\t" ": base(argument) { _responseHandler = responseHandler; _exceptionHandler = exceptionHandler; }" "\n\n"
					"\t\t\t" "public override void DoResponseHandler()" "\n"
					"\t\t\t" "{" "\n"
					"\t\t\t\t" "if (_responseHandler != null)" "\n"
					"\t\t\t\t\t" "_responseHandler(Argument as Argument);" "\n"
					"\t\t\t" "}" "\n\n"
					"\t\t\t" "public override void DoExceptionHandler()" "\n"
					"\t\t\t" "{" "\n"
					"\t\t\t\t" "if (_exceptionHandler != null)" "\n"
					"\t\t\t\t\t" "_exceptionHandler(Argument as Argument);" "\n"
					"\t\t\t\t" "else" "\n"
					"\t\t\t\t\t" "Common.CException.HandleException(new System.Exception(Argument.Controller.ErrorText));" "\n"
					"\t\t\t" "}" "\n\n"
					"\t\t\t" "private ResponseDelegate _responseHandler;" "\n"
					"\t\t\t" "private ExceptionDelegate _exceptionHandler;" "\n"
					"\t\t" "}" "\n\n";

				// Method构造和获取请求（用于设置请求参数）
				csStream << boost::format(
					"\t\t" R"(public %sMethod(IRpcChannel channel) : base("%s", 0x%s, 0x%s), _argument(new Argument) {})" "\n\n") % 
					method._name % method._name % method._id % service._id;

				csStream << 
					"\t\t" "public Request Request { get { return _argument.Request as Request; } }" "\n\n";

				// Call方法
				csStream <<
					"\t\t" "public void Call(" "\n"
					"\t\t\t" "Callback.ResponseDelegate responseHandler = null," "\n"
					"\t\t\t" "Callback.ExceptionDelegate exceptionHandler = null)" "\n"
					"\t\t" "{" "\n"
					"\t\t\t" "channel.CallMethod(this, new Callback(_argument, responseHandler, exceptionHandler));" "\n"
					"\t\t" "}" "\n"
					"\t" "}" "\n\n";

				// 服务方法
				csStream << boost::format(
					"\t" "public abstract void %s(%sMethod.Argument argument);" "\n\n") % 
					method._name % method._name;
			}

			// 写入服务构造
			csStream << boost::format("\t" R"(public %s() : base("%s", 0x%s) {})" "\n\n") %
				service._name % service._name % service._id;

			// 写入CreateMethodArgument
			csStream << "\t" "public override XyNet.RpcArgument CreateMethodArgument(int methodId)" "\n";
			"\t" "{" "\n"
			"\t\t" "switch (methodIndex)" "\n"
			"\t\t" "{" "\n";

			for (const auto& method : service._methods)
			{
				csStream << boost::format(
					"\t\t" "case %d:" "\n") % method._id;
				csStream << boost::format(
					"\t\t\t" "return new %sMethod.Argument();" "\n") % method._name;
			}

			csStream <<
				"\t\t" "default:" "\n"
				"\t\t\t" "Debug.Assert(false);" "\n"
				"\t\t\t" "return null;" "\n"
				"\t\t" "}" "\n"
				"\t" "}" "\n\n";

			// 写入CallMethod
			csStream <<
				"\t" "public override void CallMethod(int methodIndex, XyNet.RpcArgument argument)" "\n"
				"\t" "{" "\n"
				"\t\t" "switch (methodIndex)" "\n"
				"\t\t" "{" "\n";

			for (const auto& method : service._methods)
			{
				csStream << boost::format(
					"\t\t" "case %d:" "\n") % method._id;
				csStream << boost::format(
					"\t\t\t" "%s(argument as %sMethod.Argument);" "\n")
					% method._name % method._name <<
					"\t\t\t" "break;" "\n";
			}

			csStream <<
				"\t\t\t" "default:" "\n"
				"\t\t\t\t" "Debug.Assert(false);" "\n"
				"\t\t\t\t" "break;" "\n"
				"\t\t\t" "}" "\n"
				"\t\t" "}" "\n"
				"\t" "}" "\n";

			csStream << "}" "\n\n";
		}

		if (!package.empty())
		{
			csStream << "}" << endl;
		}
	}

}